package org.apache.lucene.ocean;

import java.io.File;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.io.FileUtils;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.ocean.util.Util;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.store.Lock;
import org.apache.lucene.store.LockObtainFailedException;
import org.apache.lucene.store.NativeFSLockFactory;

/**
 * DirectoryMap implementation that uses the file system.  Borrows somewhat
 * from Lucene's FSDirectory with the locking etc.
 *
 */
public class FSDirectoryMap extends DirectoryMap {
  public static final String WRITE_LOCK_NAME = "write.lock";
  private Map<String,FSDirectory> map = new HashMap<String,FSDirectory>();
  private File fileDirectory;
  private LogDirectory rootDirectory;
  private LogDirectory logDirectory;
  private static MessageDigest DIGESTER;
  private static final char[] HEX_DIGITS =
  {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
  private NativeFSLockFactory lockFactory;
  
  static {
    try {
      DIGESTER = MessageDigest.getInstance("MD5");
    } catch (NoSuchAlgorithmException e) {
        throw new RuntimeException(e.toString(), e);
    }
  }
  
  public FSDirectoryMap(File fileDirectory, String logDirectoryName) throws IOException {
    this.fileDirectory = fileDirectory;
    Util.mkdir(fileDirectory);
    
    lockFactory = new NativeFSLockFactory(fileDirectory);
    //lockFactory.clearLock(WRITE_LOCK_NAME);
    lockFactory.setLockPrefix(getLockID());
    Lock lock = lockFactory.makeLock(WRITE_LOCK_NAME);
    boolean obtained = lock.obtain(1000*5);
    System.out.println("lock obtained: "+obtained);
    if (!obtained) throw new LockObtainFailedException("Index locked for write: " + lock);
    
    rootDirectory = new FSLogDirectory(fileDirectory);
    for (File file : fileDirectory.listFiles()) {
      if (file.isDirectory() && !file.getName().equals(logDirectoryName)) {
        FSDirectory dir = FSDirectory.getDirectory(file);
        map.put(file.getName(), dir);
      }
    }
    logDirectory = new FSLogDirectory(new File(fileDirectory, logDirectoryName));
  }
  
  public String getLockID() {
    String dirName;                               // name to be hashed
    try {
      dirName = fileDirectory.getCanonicalPath();
    } catch (IOException e) {
      throw new RuntimeException(e.toString(), e);
    }

    byte digest[];
    synchronized (DIGESTER) {
      digest = DIGESTER.digest(dirName.getBytes());
    }
    StringBuilder buf = new StringBuilder();
    buf.append("ocean-");
    for (int i = 0; i < digest.length; i++) {
      int b = digest[i];
      buf.append(HEX_DIGITS[(b >> 4) & 0xf]);
      buf.append(HEX_DIGITS[b & 0xf]);
    }
    return buf.toString();
  }
  
  public LogDirectory getLogDirectory() {
    return logDirectory;
  }
  
  public LogDirectory getDirectory() {
    return rootDirectory;
  }
  
  public Directory create(String name) throws IOException {
    Directory directory = FSDirectory.getDirectory(new File(fileDirectory, name));
    directory.setLockFactory(new NativeFSLockFactory(fileDirectory));
    IndexWriter.unlock(directory);
    return directory;
  }

  public void delete(String name) throws IOException {
    FSDirectory directory = get(name);
    directory.close();
    File file = directory.getFile();
    FileUtils.deleteDirectory(file);
    map.remove(name);
  }

  public FSDirectory get(String name) throws IOException {
    return map.get(name);
  }

  public String[] list() throws IOException {
    int x = 0;
    String[] array = new String[map.size()];
    for (String string : map.keySet()) {
      array[x] = string;
      x++;
    }
    return array;
  }
  
  
}
